home *** CD-ROM | disk | FTP | other *** search
/ GFX Sensations 1 / Graphic Sensations - Volume 1.iso / tools / amiga / 3d_tools / irit40s.lha / Irit / poly3d-r / evalcolr.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-30  |  19.0 KB  |  535 lines

  1. /*****************************************************************************
  2. *   Routines to    evaluate vertices colors for all polygons in data. It is     *
  3. * assumed the parser has updated plane equations for all polygons at this    *
  4. * stage (if from reading it in, or by regenerating it), and they were mapped *
  5. * correctly using the global transformation applied to them.             *
  6. *   The vertices are updated for both Flat or Gouraud shading, and color is  *
  7. * evaluated for them, as a color index into the color table. Because of the  *
  8. * way the table is constructed and because two vertices in same polygon can  *
  9. * have two levels of same color, it is o.k. to interplated the color         *
  10. * indices (We assume no specular affects)!                     *
  11. *   As side affect, we use this pass to hash the polygon into global hash    *
  12. * table PolyHasTable, sorted by their Ymin values.                 *
  13. *                                         *
  14. * Written by:  Gershon Elber                Ver 2.0, Mar. 1990   *
  15. *****************************************************************************/
  16.  
  17. #include <math.h>
  18. #include <stdio.h>
  19. #include <string.h>
  20. #include <time.h>
  21. #include "program.h"
  22. #include "iritprsr.h"
  23.  
  24. #define    MIN_VERTEX_HASH_SIZE    1000  /* Minimum number of entries in table. */
  25.  
  26. typedef struct VertexHashStruct {     /* Used to hash an object vertices. */
  27.     int Entry;
  28.     RealType Normal[3];
  29.     IPVertexStruct *PVertex;
  30. } VertexHashStruct;
  31.  
  32. static RealType MinNormalDotProd; /* Min. normal dot prod to accept as same. */
  33. static int VertexHashSize = 0;    /* Size of vertex hash table size allocated. */
  34. static int PolyCount = 0;    /* Number of polygons processed in this module: */
  35.  
  36. static void PrepareAllObjects(IPObjectStruct *PObject);
  37. static void PrepareOnePolygonObject(IPObjectStruct *PObject);
  38. static void AverageVrtxNormals(IPObjectStruct *PObject);
  39. static void InsertVerticesToHashTable(VertexHashStruct *VerticesHashTable,
  40.                               IPObjectStruct *PObject);
  41. static void UpdateVerticesFromHashTable(VertexHashStruct *VerticesHashTable,
  42.                               IPObjectStruct *PObject);
  43. static int SameVertexAndNormals(IPVertexStruct *PVertex1, RealType *Normal1,
  44.                           VertexHashStruct *PHVertex2);
  45. static RealType EvalNormalsAngle(IPVertexStruct *PVertex1, RealType *Normal1,
  46.                           VertexHashStruct *PHVertex2);
  47. static int EvalVertexKey(IPVertexStruct *PVertex);
  48. static void EvalOnePolygon(IPPolygonStruct *PPolygon, int ColorIndex);
  49. static int CosineShading(RealType *Normal, int ColorIndex);
  50.  
  51. /*****************************************************************************
  52. * Routine to evaluate the vertices colors for both Flat/Gouraud shading, as  *
  53. * indices for the color table.                             *
  54. *****************************************************************************/
  55. void EvalVrtxColors(IPObjectStruct *PObjects)
  56. {
  57.     int    i;
  58.     long
  59.     SaveTime = time(NULL);
  60.  
  61.     PolyHashTable = (IPPolygonStruct **)
  62.     IritMalloc(sizeof(IPPolygonStruct *) * GlblShadeInfo.ScrnYSize *
  63.            GlblShadeInfo.SubSamplePixel);
  64.  
  65.     for (i = 0;
  66.      i < GlblShadeInfo.ScrnYSize * GlblShadeInfo.SubSamplePixel;
  67.      i++)
  68.     PolyHashTable[i] = NULL;
  69.  
  70.     fprintf(stderr, "\nPass 3, Polys [%4d] =      ", GlblNumOfPolys);
  71.  
  72.     PrepareAllObjects(PObjects);
  73.  
  74.     fprintf(stderr, ",  %ld seconds.", time(NULL) - SaveTime);
  75. #ifdef AMIGA
  76.     fflush(stderr);
  77. #endif /* AMIGA */
  78. }
  79.  
  80. /*****************************************************************************
  81. * Scan all objects.                                 *
  82. *****************************************************************************/
  83. static void PrepareAllObjects(IPObjectStruct *PObjects)
  84. {
  85.     while (PObjects) {
  86.     if (IP_IS_POLY_OBJ(PObjects) && IP_IS_POLYGON_OBJ(PObjects))
  87.         PrepareOnePolygonObject(PObjects);
  88.     PObjects = PObjects -> Pnext;
  89.     }
  90. }
  91.  
  92. /*****************************************************************************
  93. * Routine to prepare one object Object.                         *
  94. *****************************************************************************/
  95. static void PrepareOnePolygonObject(IPObjectStruct *PObject)
  96. {
  97.     static int
  98.     WasPolyline = FALSE;
  99.     int Level,
  100.     ColorIndex = AttrGetObjectColor(PObject);
  101.     IPPolygonStruct *Ptemp,
  102.     *PList = PObject -> U.Pl;
  103.  
  104.     if (GlblShadeInfo.Gouraud) {
  105.     /* Need to average all vertices normals from adjacent polygons,      */
  106.     /* so we can interpolate them, and select color for them directly.   */
  107.     AverageVrtxNormals(PObject);
  108.     }
  109.  
  110.     while (PList) {
  111.     Ptemp = PList -> Pnext;
  112.  
  113.     EvalOnePolygon(PList, ColorIndex);
  114.  
  115.     /* And add polygon into polygon hash table sorted by Ymin: */
  116.     if (IP_IS_POLYGON_OBJ(PObject)) {
  117.         /* If BackFacing is TRUE, and this polygon has plane         */
  118.         /* suggesting it is back facing - dont insert to hash table. */
  119.         if (!(GlblShadeInfo.BackFacing && PList -> Plane[2] > 0.0)) {
  120.         Level = PList -> BBox[0][1];
  121.         Level = BOUND(Level, 0, GlblShadeInfo.ScrnYSize *
  122.                  GlblShadeInfo.SubSamplePixel); /* Be 100% safe. */
  123.         if (Level < GlblShadeInfo.ScrnYSize *
  124.                         GlblShadeInfo.SubSamplePixel) {
  125.             /* Concat to hash table at level Level: */
  126.             PList -> Pnext = PolyHashTable[Level];
  127.             PolyHashTable[Level] = PList;
  128.             }
  129.         }
  130.         else {
  131.         if (!GlblShadeInfo.Gouraud)
  132.         PolyCount--;                   /* One less poly. */
  133.         }
  134.     }
  135.     else if (IP_IS_POLYLINE_OBJ(PObject)) {
  136.         if (!WasPolyline) {
  137.         WasPolyline = TRUE;
  138.         if (GlblMore)
  139.             fprintf(stderr, "\nPolylines are not supported, ignored.\n");
  140.         }
  141.     }
  142.  
  143.     PList =    Ptemp;
  144.     }
  145. }
  146.  
  147. /*****************************************************************************
  148. * Routine to update average normals of adjacent polygons at common vertices  *
  149. * so gouraud shading can be applied. Does 2 passes on all object vertices:   *
  150. * 1. Combine (and average) common vertices - vertices with normal no differ  *
  151. *    more than GlblShadeInfo.NrmlAvgDegree degrees.                 *
  152. * 2. For each vertex find its closest averaged normal as evaluated at 1, and *
  153. *    calculate color for it.                             *
  154. *****************************************************************************/
  155. static void AverageVrtxNormals(IPObjectStruct *PObject)
  156. {
  157.     int i;
  158.     VertexHashStruct *VerticesHashTable;
  159.  
  160.     /* Prepare maximum degree allowed to merge two normals in cosine form:   */
  161.     MinNormalDotProd = cos(GlblShadeInfo.NrmlAvgDegree * M_PI / 180.0);
  162.  
  163.     /* Allocate hash table twice as big as number of possible entries to     */
  164.     /* reduce the average hit ratio, with minimum of MIN_VERTEX_HASH_SIZE.   */
  165.     VertexHashSize = MAX(GlblNumOfVerts * 2, MIN_VERTEX_HASH_SIZE);
  166.     VerticesHashTable = (VertexHashStruct *)
  167.     IritMalloc(VertexHashSize * sizeof(VertexHashStruct));
  168.     for (i = 0; i < VertexHashSize; i++)
  169.     VerticesHashTable[i].Entry = 0;
  170.  
  171.     InsertVerticesToHashTable(VerticesHashTable, PObject);
  172.  
  173.     UpdateVerticesFromHashTable(VerticesHashTable, PObject);
  174.  
  175.     IritFree((VoidPtr) VerticesHashTable);
  176. }
  177.  
  178. /*****************************************************************************
  179. * Routine to insert all vertices in object into the hash table. Each vertex  *
  180. * is entered at place (key) as select via EvalVertexKey routine. If no place *
  181. * the next ones are scaned until free (NULL) is found (no double key fansy   *
  182. * hashing techniques...). Note however that while scanning the non NULL      *
  183. * entries the vertex position is compared for equality, and its normal for   *
  184. * equality upto AvgNormalDegree, and if both hold, the two are merged into   *
  185. * one vertex in that position but with averaged normal.                 *
  186. *****************************************************************************/
  187. static void InsertVerticesToHashTable(VertexHashStruct *VerticesHashTable,
  188.                               IPObjectStruct *PObject)
  189. {
  190.     int i, Key;
  191.     RealType EntryRatio, *Normal, *OldNormal;
  192.     IPPolygonStruct *PPolygon;
  193.     IPVertexStruct *PVertex;
  194.  
  195.     for (PPolygon = PObject -> U.Pl;
  196.      PPolygon != NULL;
  197.      PPolygon = PPolygon -> Pnext) {
  198.     Normal = PPolygon -> Plane;
  199.  
  200.     /* If BackFacing is TRUE, and this polygon has plane        */
  201.     /* suggesting it is back facing - dont count it.        */
  202.     if (!(GlblShadeInfo.BackFacing && PPolygon -> Plane[2] < 0.0))
  203.         PolyCount++;
  204.     fprintf(stderr, "\b\b\b\b\b%5d", PolyCount);
  205.  
  206.  
  207.     for (PVertex = PPolygon -> PVertex;
  208.          PVertex != NULL;
  209.          PVertex = PVertex -> Pnext) {
  210.         Key = EvalVertexKey(PVertex);
  211.         while (VerticesHashTable[Key].Entry != 0) {
  212.         /* Test if should be combined with old vertex: */
  213.         if (SameVertexAndNormals(PVertex, Normal,
  214.                             &VerticesHashTable[Key])) {
  215.             /* Megre the normals: */
  216.             EntryRatio = 1.0 / ++VerticesHashTable[Key].Entry;
  217.             OldNormal = VerticesHashTable[Key].Normal;
  218.             for (i = 0; i < 3; i++) OldNormal[i] =
  219.             OldNormal[i] * (1.0 - EntryRatio) +
  220.             Normal[i] * EntryRatio;
  221.             break;
  222.         }
  223.         Key = ++Key % VertexHashSize;
  224.         }
  225.         if (VerticesHashTable[Key].Entry == 0) {
  226.         /* Could not merge the vertex with old one - do it now: */
  227.         VerticesHashTable[Key].PVertex = PVertex;
  228.         GEN_COPY(VerticesHashTable[Key].Normal, Normal,
  229.                               3 * sizeof(RealType));
  230.         ++VerticesHashTable[Key].Entry;
  231.         }
  232.     }
  233.     }
  234. }
  235.  
  236. /*****************************************************************************
  237. * Routine to scan all vertices in object and update their normals to the     *
  238. * close one at that point as found in the hash table.                 *
  239. *****************************************************************************/
  240. static void UpdateVerticesFromHashTable(VertexHashStruct *VerticesHashTable,
  241.                               IPObjectStruct *PObject)
  242. {
  243.     static int
  244.     Flip = FALSE;
  245.     int Key;
  246.     RealType *Normal, MaxCosAngle, CosAngle;
  247.     IPPolygonStruct *PPolygon;
  248.     IPVertexStruct *PVertex;
  249.     VertexHashStruct *PHVertex;
  250.  
  251.     for (PPolygon = PObject -> U.Pl;
  252.      PPolygon != NULL;
  253.      PPolygon = PPolygon -> Pnext) {
  254.     Normal = PPolygon -> Plane;
  255.  
  256.     if (Flip) {
  257.         Flip = FALSE;
  258.         fprintf(stderr, "a\b");
  259.     }
  260.     else {
  261.         Flip = TRUE;
  262.         fprintf(stderr, "A\b");
  263.     }
  264.  
  265.     PVertex = PPolygon -> PVertex;
  266.     for (PVertex = PPolygon -> PVertex;
  267.          PVertex != NULL;
  268.          PVertex = PVertex -> Pnext) {
  269.         PHVertex = NULL;
  270.         MaxCosAngle = -INFINITY;
  271.         Key = EvalVertexKey(PVertex);
  272.         while (VerticesHashTable[Key].Entry != 0) {
  273.         /* Search for the closest Normal at this vertex point: */
  274.         if ((CosAngle = (EvalNormalsAngle(PVertex, Normal,
  275.                     &VerticesHashTable[Key]))) > MaxCosAngle) {
  276.             PHVertex = &VerticesHashTable[Key];
  277.             MaxCosAngle = CosAngle;
  278.         }
  279.         Key = ++Key % VertexHashSize;
  280.         }
  281.         if (IP_HAS_NORMAL_VRTX(PVertex)) {
  282.         /* Use defined normal for this vertex, if has one. */
  283.         PVertex -> Color = CosineShading(PVertex -> Normal,
  284.                          AttrGetObjectColor(PObject));
  285.         }
  286.         else if (PHVertex) {
  287.         /* Should always be non NULL but who knows... */
  288.         PVertex -> Color = CosineShading(PHVertex -> Normal,
  289.                          AttrGetObjectColor(PObject));
  290.         }
  291.         else {
  292.         PVertex -> Color = CosineShading(PPolygon -> Plane,
  293.                          AttrGetObjectColor(PObject));
  294.         }
  295.     }
  296.     }
  297. }
  298.  
  299. /*****************************************************************************
  300. * Routine to compare two vertices to be exactly equal in their position and  *
  301. * to have same normal up to GlblShadeInfo.NrmlAvgDegree.             *
  302. *****************************************************************************/
  303. static int SameVertexAndNormals(IPVertexStruct *PVertex1, RealType *Normal1,
  304.                         VertexHashStruct *PHVertex2)
  305. {
  306.     RealType *Normal2,
  307.     *Coord1 = PVertex1 -> Coord,
  308.     *Coord2 = PHVertex2 -> PVertex -> Coord;
  309.  
  310.     if (!APX_EQ(Coord1[0], Coord2[0]) ||
  311.     !APX_EQ(Coord1[1], Coord2[1]) ||
  312.     !APX_EQ(Coord1[2], Coord2[2]))
  313.     return FALSE;
  314.  
  315.     Normal2 = PHVertex2 -> Normal;
  316.  
  317.     return (Normal1[0] * Normal2[0] +
  318.         Normal1[1] * Normal2[1] +
  319.         Normal1[2] * Normal2[2] > MinNormalDotProd);
  320. }
  321.  
  322. /*****************************************************************************
  323. * Routine to evaluate the angle between the given two vertices if they are   *
  324. * the same, and return the angle cosine value. (-INFINITY is returned if     *
  325. * not same point...).                                 *
  326. *****************************************************************************/
  327. static RealType EvalNormalsAngle(IPVertexStruct *PVertex1, RealType *Normal1,
  328.                         VertexHashStruct *PHVertex2)
  329. {
  330.     RealType *Normal2,
  331.     *Coord1 = PVertex1 -> Coord,
  332.     *Coord2 = PHVertex2 -> PVertex -> Coord;
  333.  
  334.     if (!APX_EQ(Coord1[0], Coord2[0]) ||
  335.     !APX_EQ(Coord1[1], Coord2[1]) ||
  336.     !APX_EQ(Coord1[2], Coord2[2]))
  337.     return -INFINITY;
  338.  
  339.     Normal2 = PHVertex2 -> Normal;
  340.  
  341.     return Normal1[0] * Normal2[0] +
  342.        Normal1[1] * Normal2[1] +
  343.        Normal1[2] * Normal2[2];
  344. }
  345.  
  346. /*****************************************************************************
  347. * Routine to evaluate integer key in the range 0 .. VertexHashSize - 1         *
  348. * for the given vertex PVertex.                             *
  349. *****************************************************************************/
  350. static int EvalVertexKey(IPVertexStruct *PVertex)
  351. {
  352.     int Key;
  353.     RealType
  354.     *Coord = PVertex -> Coord;
  355.  
  356.     Key = ((int) (((long) (Coord[0] * 239 + Coord[1] * 677 + Coord[2] * 109)) %
  357.                             VertexHashSize));
  358.  
  359.     return Key;
  360. }
  361.  
  362. /*****************************************************************************
  363. * Routine to update the vertices colors, which are indices tp the ColorMap   *
  364. * as save in global GlblShadeInfo structure. Given Object color (as index    *
  365. * into table), the exact level is evaluationed using each polygon normal.    *
  366. *****************************************************************************/
  367. static void EvalOnePolygon(IPPolygonStruct *PPolygon, int ColorIndex)
  368. {
  369.     int Color;
  370.     IPVertexStruct
  371.     *V = PPolygon -> PVertex;
  372.  
  373.     if (GlblShadeInfo.Gouraud) {
  374.     /* Vertices were already updated by the Object averaging of normals  */
  375.     /* routine (see AverageVrtxNormals routine).                 */
  376.     }
  377.     else {
  378.     fprintf(stderr, "\b\b\b\b\b%5d", ++PolyCount);
  379.  
  380.     /* This is much easier - set all vertices color to same Color.       */
  381.     Color = CosineShading(PPolygon -> Plane, ColorIndex);
  382.     for (; V != NULL; V = V -> Pnext)
  383.         V -> Color = Color;
  384.     }
  385. }
  386.  
  387. /*****************************************************************************
  388. * Routine to evaluate cosine shading given a normal vector. Uses the global  *
  389. * shading structure GlblShadeInfo to evaluate it. Returns a color from the   *
  390. * color map defined for the current scene.                     *
  391. * It is assumed the given normal is normalized to size 1.0, and that the     *
  392. * intensity levels of the Object Color are saved in indices PObject -> Color *
  393. * to PObject -> Color + GlblShadeInfo.LevelsPerColor - 1. These colors are   *
  394. * interpolated to begin from the ambient level, so we dont have to consider  *
  395. * ambient light here - just the cosine factor.                     *
  396. *****************************************************************************/
  397. static int CosineShading(RealType *Normal, int ColorIndex)
  398. {
  399.     int NewColorIndex;
  400.     RealType Intensity;
  401.  
  402.     if (GlblShadeInfo.TwoSources) {
  403.     /* Take the absolute value of the dot product as intensity: */
  404.     Intensity = Normal[0] * GlblShadeInfo.LightSource[0] +
  405.             Normal[1] * GlblShadeInfo.LightSource[1] +
  406.             Normal[2] * GlblShadeInfo.LightSource[2];
  407.     Intensity = ABS(Intensity);
  408.     }
  409.     else {
  410.     /* Dot product of two normals is in [-1..1] range. Make it [0..1]: */
  411.     Intensity = (Normal[0] * GlblShadeInfo.LightSource[0] +
  412.              Normal[1] * GlblShadeInfo.LightSource[1] +
  413.              Normal[2] * GlblShadeInfo.LightSource[2] + 1.0) * 0.5;
  414.     }
  415.  
  416.     NewColorIndex = ColorIndex +
  417.           ((int) ((GlblShadeInfo.LevelsPerColor - 1) * (1.0 - Intensity)));
  418.     /* This should never happen, but if it does, the error is so fatal       */
  419.     /* (generate different color tone instead...) we double check this one.  */
  420.     return BOUND(NewColorIndex, ColorIndex,
  421.                 ColorIndex + GlblShadeInfo.LevelsPerColor - 1);
  422. }
  423.  
  424. /*****************************************************************************
  425. * Routine to update plane equation of the given    polygon:             *
  426. *   It is assumed that at list 3 points in polygon do exists, and pick the   *
  427. * tuple that has biggest length for maximum accuracy.                 *
  428. *   Note the polygon must be convex.                         *
  429. *   Returns FALSE if failed to evaluate the PLANE equation.             *
  430. *****************************************************************************/
  431. int UpdateEqnPolygon(IPPolygonStruct *PPolygon, int SetFlipDir)
  432. {
  433.     int    i, FlipPlaneDir;
  434.     RealType Len, V1[3], V2[3], *Coord, *CoordNext, *CoordNextNext, Plane[3],
  435.     MaxPlane[3],
  436.     MaxLen = 0.0;
  437.     IPVertexStruct
  438.     *VList = PPolygon -> PVertex;
  439.  
  440.     /* Search for 3 consequtive non-colinear point from polygon: */
  441.     for (; VList -> Pnext -> Pnext != NULL; VList = VList -> Pnext) {
  442.     Coord = VList -> Coord;
  443.     CoordNext = VList -> Pnext -> Coord;
  444.     CoordNextNext = VList -> Pnext -> Pnext -> Coord;
  445.  
  446.     if (!PT_APX_EQ(Coord, CoordNext) &&
  447.         !PT_APX_EQ(CoordNext, CoordNextNext)) {
  448.         for (i = 0; i < 3; i++) {/* Prepare two vectors on polygon plane.*/
  449.         V1[i] = Coord[i] - CoordNext[i];
  450.         V2[i] = CoordNext[i] - CoordNextNext[i];
  451.         }
  452.  
  453.         /* Find plane normal by a cross product of two vectors on plane: */
  454.         Plane[0] = V1[1] * V2[2] - V1[2] * V2[1];
  455.         Plane[1] = V1[2] * V2[0] - V1[0] * V2[2];
  456.         Plane[2] = V1[0] * V2[1] - V1[1] * V2[0];
  457.  
  458.         /* Find vector Len. - we are looking for the biggest: */
  459.         Len = sqrt(SQR(Plane[0]) + SQR(Plane[1]) + SQR(Plane[2]));
  460.         if (Len > MaxLen) {
  461.         for (i = 0; i < 3; i++)
  462.             MaxPlane[i] = Plane[i];
  463.         MaxLen = Len;
  464.         }
  465.     }
  466.     }
  467.  
  468.     if (ABS(MaxLen) < SQR(EPSILON)) { /* Fail to find 3 non-colinear points. */
  469.     if (GlblMore) {
  470.         fprintf(stderr,
  471.         "\nError: Invalid polygon (%d) found in file (zero edge length/colinear vertices):\n",
  472.         PolyCount);
  473.         PrintPolyContent(PPolygon);
  474.     }
  475.     PPolygon -> Plane[0] = PPolygon -> Plane[1] = 0.0;  /* Pick Z = 1.0. */
  476.     PPolygon -> Plane[2] = PPolygon -> Plane[2] = 1.0;
  477.     return FALSE;
  478.     }
  479.  
  480.     if (SetFlipDir) {
  481.     FlipPlaneDir =
  482.          PPolygon -> Plane[0] * MaxPlane[0] +
  483.          PPolygon -> Plane[1] * MaxPlane[1] +
  484.          PPolygon -> Plane[2] * MaxPlane[2] < 0.0;
  485.     }
  486.     else
  487.     FlipPlaneDir = 1;
  488.  
  489.     for (i = 0; i < 3; i++)
  490.     PPolygon -> Plane[i] = (FlipPlaneDir ? -1 : 1)
  491.                         * MaxPlane[i] / MaxLen;
  492.  
  493.     PPolygon ->    Plane[3] =
  494.     (- Coord[0] * PPolygon -> Plane[0]
  495.      - Coord[1] * PPolygon -> Plane[1]
  496.      - Coord[2] * PPolygon -> Plane[2]);
  497.  
  498.     return TRUE;
  499. }
  500.  
  501. /*****************************************************************************
  502. * Routine to evaluate the cross    product    of 3 points projected to Z = 0 plane *
  503. * and return the sign of the result (Only Z component).                 *
  504. *****************************************************************************/
  505. int CrossProd(RealType Pt1[3], RealType Pt2[3], RealType Pt3[3])
  506. {
  507.     RealType Zout;
  508.  
  509.     /* U = Pt2 - Pt1,  V = Pt3 - Pt2,        Zoutput    = Ux * Vy - Uy * Vx. */
  510.     Zout = (Pt2[0] - Pt1[0]) /*    Ux */  * (Pt3[1] - Pt2[1]) /* Vy */  -
  511.        (Pt2[1] - Pt1[1]) /*    Uy */  * (Pt3[0] - Pt2[0]) /* Vx */;
  512.     if (APX_EQ(Zout, 0.0))
  513.     return 0;
  514.     if (Zout < 0.0)
  515.     return -1;
  516.     else
  517.     return 1;
  518. }
  519.  
  520. /*****************************************************************************
  521. * Routine to print the content of a given edge:                     *
  522. *****************************************************************************/
  523. void PrintPolyContent(IPPolygonStruct *PPoly)
  524. {
  525.     IPVertexStruct
  526.     *VList = PPoly -> PVertex;
  527.  
  528.     for (; VList != NULL; VList = VList -> Pnext) {
  529.     fprintf(stderr, "   %12f %12f %12f\n",
  530.         VList -> Coord[0],
  531.         VList -> Coord[1],
  532.         VList -> Coord[2]);
  533.     }
  534. }
  535.